package com.maikobi.maikobi.exception;

import com.maikobi.maikobi.common.BaseResponse;
import com.maikobi.maikobi.common.ErrorCode;
import com.maikobi.maikobi.common.ResultUtils;
import jakarta.validation.ConstraintViolationException;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.MissingPathVariableException;
import org.springframework.web.bind.MissingServletRequestParameterException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;

/**
 * 全局异常处理器，统一处理项目中抛出的各种异常，并返回标准响应格式
 * @author: Maiko7
 * @create: 2025-07-13 15:23
 */

@RestControllerAdvice
@Slf4j
public class GlobalExceptionHandler {

    /**
     * 处理自定义业务异常（通常由业务逻辑主动抛出）
     * <p>
     * 该异常用于封装业务层出现的可预见错误，比如用户权限不足、资源不存在、操作冲突等。
     * 通过捕获 BusinessException，可以返回带有自定义错误码、错误信息及详细描述的统一响应。
     *
     * @param e 自定义业务异常对象，包含错误码、消息及描述信息
     * @return BaseResponse 包含业务异常的错误码、错误信息和详细描述
     */
    @ExceptionHandler(BusinessException.class)
    public BaseResponse<?> businessExceptionHandler(BusinessException e) {
        log.error("业务异常 BusinessException：code={}, message={}, description={}",
                e.getCode(), e.getMessage(), e.getDescription(), e);
        return ResultUtils.error(e.getCode(), e.getMessage(), e.getDescription());
    }

    /**
     * 处理请求体参数校验失败异常（针对 @Valid 注解标注的请求体对象）
     * <p>
     * 当前端请求中携带的 JSON 对象字段不满足如 @NotBlank、@Email 等校验注解的要求时，
     * Spring MVC 会抛出 MethodArgumentNotValidException。此处统一捕获，提取首个校验失败字段的错误消息，
     * 并返回给前端，便于前端用户快速定位输入错误。
     *
     * 示例：
     * 接口定义：public ResponseEntity register(@RequestBody @Valid UserRegisterRequest request)
     * 如果 request 中 email 字段格式错误，则触发此异常。
     *
     * @param e 方法参数无效异常，包含校验错误信息
     * @return BaseResponse 返回带有校验失败提示的标准错误响应
     */
    @ExceptionHandler(MethodArgumentNotValidException.class)
    public BaseResponse<?> handleMethodArgumentNotValidException(MethodArgumentNotValidException e) {
        String errorMessage = e.getBindingResult().getFieldError().getDefaultMessage();
        log.error("参数校验失败 MethodArgumentNotValidException：{}", errorMessage, e);
        return ResultUtils.error(ErrorCode.VALIDATION_ERROR, errorMessage);
    }


    /**
     * 处理请求参数缺失异常（针对 @RequestParam 标注的参数）
     * <p>
     * 当前端请求缺少必须的查询参数（如 ?account=xxx）时，Spring MVC 会抛出 MissingServletRequestParameterException。
     * 此处统一捕获该异常，提取缺失参数的名称和类型，返回标准错误响应。
     *
     * 示例：
     * 接口定义：public ResponseEntity getUser(@RequestParam String account)
     * 若前端未传 account 参数，则会抛出该异常。
     *
     * @param e 请求参数缺失异常，包含缺失参数的名称和类型
     * @return BaseResponse 返回带有缺失参数提示的错误响应
     */
    @ExceptionHandler(MissingServletRequestParameterException.class)
    public BaseResponse<?> handleMissingServletRequestParameterException(MissingServletRequestParameterException e) {
        log.error("请求参数缺失 MissingServletRequestParameterException: 缺失参数名={}, 类型={}",
                e.getParameterName(), e.getParameterType(), e);
        return ResultUtils.error(ErrorCode.VALIDATION_ERROR, "缺少必要的请求参数：" + e.getParameterName());
    }


    /**
     * 处理请求参数校验失败异常（@RequestParam、@PathVariable 验证失败）
     */
    @ExceptionHandler(ConstraintViolationException.class)
    public BaseResponse<?> handleConstraintViolationException(ConstraintViolationException e) {
        // 返回所有错误信息拼接
        String errorMsg = e.getConstraintViolations()
                .stream()
                .map(cv -> cv.getMessage())
                .reduce((s1, s2) -> s1 + "; " + s2)
                .orElse("参数校验失败");
        log.error("参数校验失败 ConstraintViolationException：{}", errorMsg, e);
        return ResultUtils.error(ErrorCode.VALIDATION_ERROR, errorMsg);
    }

    /**
     * 处理路径变量缺失异常（针对 @PathVariable 标注的参数）
     * <p>
     * 当前端请求路径中缺少必须的路径变量（如 /user/{id} 实际未传 id）时，Spring MVC 会抛出 MissingPathVariableException。
     * 此处统一捕获该异常，提取缺失变量的名称和类型，返回标准错误响应。
     *
     * 示例：
     * 接口定义：public ResponseEntity getUser(@PathVariable Long id)
     * 若请求路径为 /user/ 而非 /user/123，则会抛出该异常。
     *
     * @param e 路径变量缺失异常，包含缺失变量的名称和方法参数信息
     * @return BaseResponse 返回带有路径变量缺失提示的错误响应
     */
    @ExceptionHandler(MissingPathVariableException.class)
    public BaseResponse<?> handleMissingPathVariableException(MissingPathVariableException e) {
        log.error("路径变量缺失 MissingPathVariableException: 缺失变量名={}, 类型={}",
                e.getVariableName(), e.getParameter().getParameterType(), e);
        return ResultUtils.error(ErrorCode.VALIDATION_ERROR, "缺少必要的路径变量：" + e.getVariableName());
    }




    /**
     * 处理系统运行时异常（如空指针、数组越界、类型转换等）
     * <p>
     * 该方法捕获所有继承自 {@link RuntimeException} 的异常，包括常见的编程错误，如：
     * NullPointerException、ClassCastException、IllegalArgumentException 等。
     * 使用此方法可统一处理非受检异常，防止异常信息直接暴露给前端用户。
     *
     * @param e 运行时异常对象
     * @return BaseResponse 统一封装的错误响应，包含异常信息
     */
    @ExceptionHandler(RuntimeException.class)
    public BaseResponse<?> runtimeExceptionHandler(RuntimeException e) {
        log.error("运行时异常 RuntimeException：{}", e.getMessage(), e);
        return ResultUtils.error(ErrorCode.SYSTEM_ERROR, e.getMessage());
    }

    /**
     * 全局兜底异常处理器：用于处理所有未被显式捕获的异常
     * <p>
     * 当程序抛出未知异常且未被其他 @ExceptionHandler 方法捕获时，会进入该方法。
     * 通常作为最后一道防线，避免系统异常信息泄漏，同时为前端提供统一的错误格式。
     * 可用于线上环境错误日志监控、故障排查。
     *
     * @param e 未知异常对象
     * @return BaseResponse 统一封装的系统错误响应，提示用户联系管理员
     */
    @ExceptionHandler(Exception.class)
    public BaseResponse<?> defaultExceptionHandler(Exception e) {
        log.error("未知异常 Exception：{}", e.getMessage(), e);
        return ResultUtils.error(ErrorCode.SYSTEM_ERROR, "系统内部异常，请联系管理员");
    }


}
